package org.xqp.hrm.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.xqp.hrm.constants.RedisConstants;
import org.xqp.hrm.domain.CourseType;
import org.xqp.hrm.mapper.CourseTypeMapper;
import org.xqp.hrm.service.ICourseTypeService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.xqp.hrm.vo.CrumbsVo;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author whale.chen
 * @since 2021-11-26
 */
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {

    @Autowired
    private CourseTypeMapper courseTypeMapper;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    // 这个注解的意思调用方法触发清除缓存
    @CacheEvict(cacheNames = RedisConstants.KEY_PREFIX_COURSE_TYPE, key = RedisConstants.KEY_ALL_COURSE_TYPE_TREE)
    public boolean insert(CourseType entity) {
        boolean success =  super.insert(entity);
//        if(success){
//            redisTemplate.delete(RedisConstants.KEY_ALL_COURSE_TYPE_TREE);
//        }
        return success;
    }

    //数据库和Redis数据的一致性 ： 延迟双删
    @Override
    // 这个注解的意思调用方法触发清除缓存
    @CacheEvict(cacheNames = RedisConstants.KEY_PREFIX_COURSE_TYPE, key = RedisConstants.KEY_ALL_COURSE_TYPE_TREE)
    public boolean deleteById(Serializable id) {
        //1.删除数据库  - 数据库没有
        //2.有一个查询过来 - 返回的是redis中的老数据(在某些场景下可以运行短暂的脏数据)
        //3.清空Redis
        boolean success = super.deleteById(id);
//        if(success){
//            redisTemplate.delete(RedisConstants.KEY_ALL_COURSE_TYPE_TREE);
//        }
        return success;
    }

    @Override
    // 这个注解的意思调用方法触发清除缓存
    @CacheEvict(cacheNames = RedisConstants.KEY_PREFIX_COURSE_TYPE, key = RedisConstants.KEY_ALL_COURSE_TYPE_TREE)
    public boolean updateById(CourseType entity) {
        boolean success =  super.updateById(entity);
//        if(success){
//            redisTemplate.delete(RedisConstants.KEY_ALL_COURSE_TYPE_TREE);
//        }
        return success;
    }

    /**
     * 查询缓存
     * 1.先检查缓存，是否有数据
     * 2.如果缓存有，直接返回
     * 3.如果缓存没有，就查询数据库
     * 4.把数据库中的数据转成treeData
     * 5.把treeDate放到缓存
     * 6.返回数据
     */
    @Override
    // 这个方法会自动帮我们去缓存中查找数据是否存在，不存在会调用方法，获取数据，然后再将获取到的数据存到缓存中
    @Cacheable(cacheNames =RedisConstants.KEY_PREFIX_COURSE_TYPE , key = RedisConstants.KEY_ALL_COURSE_TYPE_TREE)
    public List<CourseType> treeData() {
        return list2treeFromDB();
    }

    // 在页面上添加课程后添加课程总条数
    @Override
    public void addTotalCountByIds(int count, String[] ids) {
        courseTypeMapper.addTotalCountByIds( count,  ids);
    }



    /**
     * 查询课程分类树
     * 方式一：
     * 1.查询出第一层分类
     * 2.循环每个第一层，查出第二次分类，添加到第一层的children中
     * 3.循环每个第二层，查询出第三层分类，添加到第二层的children中
     * 如何实现：如果固定层数，可以使用for循环，
     *           如果不固定层数，可以使用递归，我这里使用Mybatis的嵌套查询来做
     *           问题：额外SQL太多，性能不好
     *方式二：
     * 查询出所有分类，在代码中组装关系
     * 1.查询所有的分类
     * 2.找出所有的第一层
     * 3.其他的非底层(2,3,4,...100) 只需要找各自的上一层，加入自己上一层的children中
     * @return ：树形结构的课程分类
     */
    public List<CourseType> list2treeFromDB() {
        //return courseTypeMapper.selectTreeData(0L);

        //* 1.查询所有的分类
        List<CourseType> allCourseTypes = super.selectList(null);
        /**
         Map<Long, CourseType> allCourseTypeMap = new HashMap<>();
         allCourseTypes.stream().forEach(courseType -> {
         allCourseTypeMap.put(courseType.getId(), courseType);
         });**/
        Map<Long, CourseType> allCourseTypeMap = allCourseTypes.stream()
                .collect(Collectors.toMap(CourseType::getId, CourseType -> CourseType));
        //* 2.找出所有的第一层
        List<CourseType> firstCourseTypes = new ArrayList<>();
        allCourseTypes.forEach(courseType -> {
            //如果pid为0，就是第一层
            if(courseType.getPid() == null || courseType.getPid().longValue() == 0){
                firstCourseTypes.add(courseType);
            }else{
                //* 3.其他的非底层(2,3,4,...100) 只需要找各自的上一层，
                CourseType parentType = allCourseTypeMap.get(courseType.getPid());
                if(parentType != null){
                    parentType.getChildren().add(courseType);
                }
            }
        });
        return firstCourseTypes;
    }

    /**
     * 面包屑
     * 1.根据分类ID查询课程分类
     * 2.拿到Path.进行切割[  1037，1039，1048 ]
     * 3.批量查询课程分类
     * 4.循环课程分类，一个分类封装成一个CrumbsVo
     * 5.要为每个分类，查询它的同级分类列表，添加到CrumbsVo
     * 6.所有的CrumbsVo装到一个List返回
     * @param id  : 课程分类ID
     */
    @Override
    public List<CrumbsVo> crumbs(Long id) {
        //* 1.根据分类ID查询课程分类
        CourseType courseType = super.selectById(id);
        //* 2.拿到Path.进行切割[  1037，1039，1048 ]
        String[] idStrs = courseType.getPath().split("\\.");
        //* 3.批量查询课程分类
        List<CourseType> courseTypes = super.selectBatchIds(Arrays.asList(idStrs));
        List<CrumbsVo> result = new ArrayList<>(idStrs.length);
        courseTypes.forEach(type -> {
            CrumbsVo vo = new CrumbsVo();
            //* 4.循环课程分类，一个分类封装成一个CrumbsVo
            vo.setOwnerCourseType(type);
            //* 5.要为每个分类，查询它的同级分类列表，添加到CrumbsVo
            Wrapper<CourseType> wrapper = new EntityWrapper<>();
            wrapper.eq("pid", type.getPid());
            List<CourseType> others = super.selectList(wrapper);
            others.remove(type);
            vo.setOtherCourseTypes(others);
            //* 6.所有的CrumbsVo装到一个List返回
            result.add(vo);
        });
        return result;
    }


    public List<CourseType> treeData3() {
        //* 1.先检查缓存，是否有数据
        List<CourseType> courseTypes = (List<CourseType>)redisTemplate.opsForValue().get(RedisConstants.KEY_ALL_COURSE_TYPE_TREE);

        //* 2.如果缓存有，直接返回
        if(courseTypes != null){
            return courseTypes;
        }
        // 如果缓存没有，就查询数据库

        //* 4.把数据库中的数据转成treeData
        courseTypes = list2treeFromDB();
        //* 5.把treeDate放到缓存
        redisTemplate.opsForValue().set(RedisConstants.KEY_ALL_COURSE_TYPE_TREE,courseTypes,12, TimeUnit.HOURS );
        //* 6.返回数据
        return courseTypes;
    }

    public List<CourseType> treeData2() {

        // 在数据库中用ssql语句获取树状课程
        //return courseTypeMapper.selectTreeData(0L);

        // 先查询所有的课程类
        List<CourseType> allCourseTypes = baseMapper.selectList(null);

        // 创建一个list集合来装顶级课程
        ArrayList<CourseType> oneCourseTypes = new ArrayList<>();

        // 遍历所有的课程类，获取顶级课程
        allCourseTypes.forEach(courseType->{
            // 判断我们遍历的courseType中的pid是否为null或者为0就是顶级课程
            if(courseType.getPid() == null || courseType.getPid().longValue() == 0){
                // 将顶级课程类添加到oneCourseTypes集合
                oneCourseTypes.add(courseType);
            }else { // 接下来将剩下的课程类找到其父类课程类，并添加到到父类课程类的children中
                for (CourseType parentCourseType : allCourseTypes) { // 遍历allCourseTypes遍历的每个对象用parentCourseType接收
                    if(parentCourseType.getId().longValue() == courseType.getPid().longValue()){ // 找到courseType的父类课程对象
                        parentCourseType.getChildren().add(courseType);
                        break;
                    }
                }
            }
        });
        return oneCourseTypes;
    }

}
